package com.example.webgobang.component;

import com.example.webgobang.WebGobangApplication;
import com.example.webgobang.mapper.ChessHistoryMapper;
import com.example.webgobang.mapper.UserMapper;
import com.example.webgobang.model.domin.ChessHistory;
import com.example.webgobang.model.domin.User;
import com.example.webgobang.model.game.GameRequest;
import com.example.webgobang.model.game.GameResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import static com.example.webgobang.common.Constants.MAX_COL;
import static com.example.webgobang.common.Constants.MAX_ROW;

@Data
@Slf4j
public class Room {
    private String roomId;
    private User user1;
    private User user2;
    private Integer blackUserId;

    private final int[][] board = new int[MAX_ROW][MAX_COL]; // 0-未落子 1-黑子（user1） 2-白字（user2）

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final List<ChessPoint> chessPointList = new ArrayList<>();

    private OnlineUserManager onlineUserManager;

    private RoomManager roomManager;

    private UserMapper userMapper;

    private ChessHistoryMapper chessHistoryMapper;

    public Room() {
        roomId = UUID.randomUUID().toString();

        // 对局房间建立将会建立多个，不能是单例，不能使用springIOC注解，之中依赖的对象需手动注入
        onlineUserManager = WebGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager = WebGobangApplication.context.getBean(RoomManager.class);
        userMapper = WebGobangApplication.context.getBean(UserMapper.class);
        chessHistoryMapper = WebGobangApplication.context.getBean(ChessHistoryMapper.class);
    }

    public void putChess(GameRequest request) throws IOException {
        GameResponse response = new GameResponse();
        // 记录落子位置
        int chess = Objects.equals(request.getUserId(), user1.getId()) ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if(board[row][col] != 0) { //重复落子
            return;
        }
        board[row][col] = chess;
        chessPointList.add(new ChessPoint(row, col));
        // 打印棋盘
//        printBoard();
        // 胜负判定
        int winner = checkWinner(row, col, chess);
        // 返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinnerId(winner);

        WebSocketSession session1 = onlineUserManager.getSessionFromGameRoom(user1.getId());
        if(session1 == null) {
            response.setWinnerId(user2.getId());
            log.info("玩家1: " + user1.getUsername() + " 掉线");
        }
        WebSocketSession session2 = onlineUserManager.getSessionFromGameRoom(user2.getId());
        if(session1 == null) {
            response.setWinnerId(user1.getId());
            log.info("玩家2: " + user2.getUsername() + " 掉线");
        }

        if(session1 != null) {
            session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        if(session2 != null) {
            session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        // 若胜负已分，则从房间管理器移除房间
        if(response.getWinnerId() != 0) {
            // 记录棋谱
            String historyJson = objectMapper.writeValueAsString(chessPointList);
            ChessHistory chessHistory = makeChessHistory(historyJson, winner);
            chessHistoryMapper.insertHistory(chessHistory);
            // 更新数据库分数信息
            int winUserId = response.getWinnerId();
            int loseUserId = (response.getWinnerId().equals(user1.getId())) ? user2.getId() : user1.getId();
            userMapper.updateWinUser(winUserId);
            userMapper.updateLoseUser(loseUserId);
            roomManager.remove(roomId, user1.getId(), user2.getId());
        }
    }

    private ChessHistory makeChessHistory(String historyJson, Integer winner) {
        ChessHistory chessHistory = new ChessHistory();
        chessHistory.setRoomId(roomId);
        chessHistory.setHistory(historyJson);
        chessHistory.setUserId1(user1.getId());
        chessHistory.setUsername1(user1.getUsername());
        chessHistory.setUserId2(user2.getId());
        chessHistory.setUsername2(user2.getUsername());
        chessHistory.setBlackUserId(blackUserId);
        chessHistory.setWinnerId(winner);
        return chessHistory;
    }

    public void surrender(GameRequest request) throws IOException {
        GameResponse response = new GameResponse();
        response.setMessage("surrender");
        response.setWinnerId(Objects.equals(request.getUserId(), user1.getId()) ? user2.getId() : user1.getId());

        WebSocketSession session1 = onlineUserManager.getSessionFromGameRoom(user1.getId());
        if(session1 == null) {
            log.info("玩家1: " + user1.getUsername() + " 掉线");
        }
        WebSocketSession session2 = onlineUserManager.getSessionFromGameRoom(user2.getId());
        if(session1 == null) {
            log.info("玩家2: " + user2.getUsername() + " 掉线");
        }

        if(session1 != null) {
            session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        if(session2 != null) {
            session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }

        // 更新数据库分数信息
        int winUserId = response.getWinnerId();
        int loseUserId = (response.getWinnerId().equals(user1.getId())) ? user2.getId() : user1.getId();
        userMapper.updateWinUser(winUserId);
        userMapper.updateLoseUser(loseUserId);
        roomManager.remove(roomId, user1.getId(), user2.getId());
    }

    private void printBoard() {
        System.out.println("[current board] room id: " + roomId);
        System.out.println("==============================");
        for(int r = 0; r < MAX_ROW; r++) {
            for(int c = 0; c < MAX_COL; c++) {
                System.out.print(board[r][c] + " ");
            }
            System.out.println();
        }
        System.out.println("==============================");
    }

    // return valve: 0-胜负未分 userId-获胜玩家id
    private int checkWinner(int row, int col, int chess) {
        for (int c = col - 4; c <= col; c++) {
            try {
                if (board[row][c] == chess
                        && board[row][c + 1] == chess
                        && board[row][c + 2] == chess
                        && board[row][c + 3] == chess
                        && board[row][c + 4] == chess) {
                    return chess == 1 ? user1.getId() : user2.getId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        for (int r = row - 4; r <= row; r++) {
            try {
                if (board[r][col] == chess
                        && board[r + 1][col] == chess
                        && board[r + 2][col] == chess
                        && board[r + 3][col] == chess
                        && board[r + 4][col] == chess) {
                    return chess == 1 ? user1.getId() : user2.getId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        for (int r = row - 4, c = col - 4; r <= row && c <= col; r++, c++) {
            try {
                if (board[r][c] == chess
                        && board[r + 1][c + 1] == chess
                        && board[r + 2][c + 2] == chess
                        && board[r + 3][c + 3] == chess
                        && board[r + 4][c + 4] == chess) {
                    return chess == 1 ? user1.getId() : user2.getId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {
            try {
                if (board[r][c] == chess
                        && board[r + 1][c - 1] == chess
                        && board[r + 2][c - 2] == chess
                        && board[r + 3][c - 3] == chess
                        && board[r + 4][c - 4] == chess) {
                    return chess == 1 ? user1.getId() : user2.getId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        return 0;
    }

    @Data
    public static class ChessPoint {
        private Integer row;
        private Integer col;

        public ChessPoint(Integer row, Integer col) {
            this.row = row;
            this.col = col;
        }
    }
}
